#include "ModuleCLUTSurveyColorLimitsEntropy.h"

void ModuleCLUTSurveyColorLimitsEntropy::init(QCVModuleHandlerInterface * parent)
{
	this->parent=parent;
	parent->setAutoProcess(false);
	win=new QWidget();
	
	pb_loadFuzzyCLUT=new QPushButton(tr("Load Fuzzy CLUT"),win);
	pb_saveResults=new QPushButton(tr("Save results"),win);
	pb_askMe=new QPushButton(tr("Get flop"),win);
	pb_clear=new QPushButton(tr("Clear results"),win);
	entropy_threshold=new QSpinBox(win);
	entropy_threshold->setPrefix(tr("ent_max_strong="));
	entropy_threshold->setSuffix(tr("%"));
	entropy_threshold->setRange(0,100);
	entropy_threshold->setValue(20);
	
	entropy_threshold_mixed=new QSpinBox(win);
	entropy_threshold_mixed->setPrefix(tr("ent_min_mixed="));
	entropy_threshold_mixed->setSuffix(tr("%"));
	entropy_threshold_mixed->setRange(0,100);
	entropy_threshold_mixed->setValue(60);
	
	
	pbBarFlop=new QProgressBar(win);
	
	sb_nbColorsAsked=new QSpinBox(win);
	sb_nbColorsAsked->setPrefix(tr("nbColorsPerFlop="));
	sb_nbColorsAsked->setRange(1,25);
	sb_nbColorsAsked->setValue(4);
	
	showClassif=new QCheckBox("Display classification",win); 
	
	plotcolors=new PlotColors();
	
	le_question=new QLineEdit(win);
	
	gb_flops_params=new QGroupBox(tr("Flop Parameters"),win);
	
	te_results=new QTextEdit(win);
	
	layout=new QVBoxLayout(win);
	layout->addWidget(pb_loadFuzzyCLUT);
	
	QGridLayout * glayout=new QGridLayout();
	glayout->addWidget(sb_nbColorsAsked,0,0);
	glayout->addWidget(pb_askMe,1,0);
	glayout->addWidget(entropy_threshold,0,1);
	glayout->addWidget(entropy_threshold_mixed,0,2);
	glayout->addWidget(pbBarFlop,1,1);
	glayout->addWidget(showClassif,2,0);
	gb_flops_params->setLayout(glayout);
	
	layout->addWidget(gb_flops_params);
	layout->addWidget(le_question);
	layout->addWidget(plotcolors);
	QHBoxLayout * hlayout=new QHBoxLayout();
	hlayout->addWidget(pb_clear);
	hlayout->addWidget(pb_saveResults);
	layout->addLayout(hlayout);
	layout->addWidget(te_results);
	
	connect(pb_clear,SIGNAL(clicked()),this,SLOT(s_clearResults()));
	connect(pb_loadFuzzyCLUT,SIGNAL(clicked()),this,SLOT(s_loadFuzzyCLUT()));
	connect(pb_saveResults,SIGNAL(clicked()),this,SLOT(s_saveResults()));
	connect(pb_askMe,SIGNAL(clicked()),this,SLOT(s_askMe()));
	
	connect(showClassif,SIGNAL(stateChanged(int)),this,SLOT(s_showClassif(int)));
	
	results=tr("");
	
}
////////////////////////////////////////////////////////////////////////////////
void ModuleCLUTSurveyColorLimitsEntropy::free()
{
	delete win;
}
////////////////////////////////////////////////////////////////////////////////
cv::Mat & ModuleCLUTSurveyColorLimitsEntropy::process()
{

}
////////////////////////////////////////////////////////////////////////////////
void ModuleCLUTSurveyColorLimitsEntropy::setInput(int n,const cv::Mat & m)
{
	
}
////////////////////////////////////////////////////////////////////////////////
QWidget* ModuleCLUTSurveyColorLimitsEntropy::widget()
{
	return win;
}
////////////////////////////////////////////////////////////////////////////////
QString ModuleCLUTSurveyColorLimitsEntropy::getName()
{
	return tr("Survey Type: Dual Colors Limit by Entropy");
}
////////////////////////////////////////////////////////////////////////////////
int ModuleCLUTSurveyColorLimitsEntropy::nbInputs()
{
	return 1;
}
////////////////////////////////////////////////////////////////////////////////
bool ModuleCLUTSurveyColorLimitsEntropy::setParams(QString params)
{
	QStringList list_params=params.split(";");
		
	if(list_params.size()==2)
	{
		sb_nbColorsAsked->setValue(list_params[0].toInt());
		entropy_threshold->setValue(list_params[1].toInt());
		return true;
	}
	else
	{
		return false;
	}	
}
////////////////////////////////////////////////////////////////////////////////
QString ModuleCLUTSurveyColorLimitsEntropy::getParams()
{
	return QString::number(sb_nbColorsAsked->value())+tr(";")+
		   QString::number(entropy_threshold->value());
}
////////////////////////////////////////////////////////////////////////////////
QCVModuleInterface * ModuleCLUTSurveyColorLimitsEntropy::clone()
{
	return new ModuleCLUTSurveyColorLimitsEntropy;
}
////////////////////////////////////////////////////////////////////////////////
QString ModuleCLUTSurveyColorLimitsEntropy::help(int id)
{
	if(id==-2)
	{
		return tr("This module provide an interface to make a survey in order to find the limit between chroatic and achromatic colors");
	}
	else if(id==-1)
	{
		return tr("image");
	}
	else if(id==0)
	{
		return tr("image");
	}
	else
	{
		return tr("erreur");
	}
}
////////////////////////////////////////////////////////////////////////////////
void ModuleCLUTSurveyColorLimitsEntropy::s_loadFuzzyCLUT()
{
	QString fn=QFileDialog::getOpenFileName(win,"Charger une Fuzzy CLUT","data","*.txt");
	if(!fn.isEmpty())
	{
		if(loadFuzzyCLUT(fn,fuzzyclut))
		{
			maxFuzzyCLUT_2_CLUT(fuzzyclut,clut);
		}
	}
}
////////////////////////////////////////////////////////////////////////////////
void ModuleCLUTSurveyColorLimitsEntropy::s_saveResults()
{
	QString fn=QFileDialog::getSaveFileName(win,"Charger une Fuzzy CLUT","data","*.txt");
	if(!fn.isEmpty())
	{
		QFile file(fn);
		if (file.open(QIODevice::WriteOnly | QIODevice::Text))
		{
			QTextStream out(&file);
			out << results;
			file.close();
		}
	}
}
////////////////////////////////////////////////////////////////////////////////
void ModuleCLUTSurveyColorLimitsEntropy::s_askMe()
{
	plotcolors->update();
	srand(time(0));
	QString r = plotcolors->getResults();
	if(!r.isEmpty())
	{
		results+=r;
	}
	te_results->setText(results);
	
	if(!clut.empty())
	{
		//On efface le jeu de couleur precedent
		plotcolors->clearColors();
		
		//On choisi deux categories de couleurs qui partagent une frontiere
		color1=rand()%11;
		cv::Mat_<float> adjenceMat;
		getConnexGraph(clut,fuzzyclut.cols,50,adjenceMat);
		do{color2=rand()%11;}while(adjenceMat(color1,color2)==0.0);
		plotcolors->setColors(color1,color2);
		
		le_question->setText(tr(" Which are ")+getC11Name(color2)+tr("?"));
		
		//On construit deux listes de couleurs une de chaque cathegorie
		QList<QColor> colors1;
		QList<QColor> colors2;
		
		//On calule egalement les entropies min et max des deux groupes
		uchar minEntropyColor1=255,maxEntropyColor1=0;
		uchar minEntropyColor2=255,maxEntropyColor2=0;
		cv::Mat entropyclut;
		entropyFuzzyCLUT_2_CLUT(fuzzyclut,entropyclut);
		
		pbBarFlop->setRange(0,round(pow(fuzzyclut.rows,1.0/3.0)));
		int p=(int)256/round(pow(fuzzyclut.rows,1.0/3.0));
		
		for(int r=0;r<256;r+=p)
		{
			for(int g=0;g<256;g+=p)
			{
				for(int b=0;b<256;b+=p)
				{
					if( CLUT(clut,r,g,b)==color1 )
					{
						colors1+=QColor(r,g,b,CLUT(clut,r,g,b));
						float entropy=CLUT(entropyclut,r,g,b);
						if(entropy>maxEntropyColor1)maxEntropyColor1=entropy;
						if(entropy<minEntropyColor1)minEntropyColor1=entropy;
					}
					else if( CLUT(clut,r,g,b)==color2 )
					{
						colors2+=QColor(r,g,b,CLUT(clut,r,g,b));
						float entropy=CLUT(entropyclut,r,g,b);
						if(entropy>maxEntropyColor2)maxEntropyColor2=entropy;
						if(entropy<minEntropyColor2)minEntropyColor2=entropy;
					}
				}
			}
			pbBarFlop->setValue(r);
		}
		
		//Puis on supprime dans les deux listes precedente les couleur dont l'entropie est superieur a s%(max-min)+min
		uchar threshold_entropy1=(uchar)(entropy_threshold->value()*(maxEntropyColor1-minEntropyColor1)/100.0+minEntropyColor1);
		uchar threshold_entropy2=(uchar)(entropy_threshold->value()*(maxEntropyColor2-minEntropyColor2)/100.0+minEntropyColor2);
		
		for(int i=0;i<colors1.size();i++)
		{
			if(CLUT(entropyclut,colors1[i].red(),colors1[i].green(),colors1[i].blue())>threshold_entropy1)
			{
				colors1.removeAt(i);
				i--;
			}
		}
		for(int i=0;i<colors2.size();i++)
		{
			if(CLUT(entropyclut,colors2[i].red(),colors2[i].green(),colors2[i].blue())>threshold_entropy2)
			{
				colors2.removeAt(i);
				i--;
			}
		}
		
		//on tire au hasard deux couleurs parmis les deux listes construites (une dans chaque liste)
		QColor color1=colors1[rand()%colors1.size()];
		QColor color2=colors2[rand()%colors2.size()];
		
		QList<QColor> line3D;
		//On trace maintenant une ligne 3D entre ces deux couleurs Bresenham
		bresenham_line_3D(color1,color2,line3D);	
		
		//on recherche le min et max de l'entopie sur cette trajectoire
		uchar minEntropyColorMixed=255;
		uchar maxEntropyColorMixed=0;
		for(int i=0;i<line3D.size();i++)
		{
			float entropy=CLUT(entropyclut,line3D[i].red(),line3D[i].green(),line3D[i].blue());
			if(entropy>maxEntropyColorMixed)maxEntropyColorMixed=entropy;
		    if(entropy<minEntropyColorMixed)minEntropyColorMixed=entropy;
		}
		
		//on seupprime de la liste les couleur dont l'entropie est trop faible
		uchar threshold_entropy_mixed=(uchar)(entropy_threshold_mixed->value()*(maxEntropyColorMixed-minEntropyColorMixed)/100.0+minEntropyColorMixed);
		for(int i=0;i<line3D.size();i++)
		{
			float entropy=CLUT(entropyclut,line3D[i].red(),line3D[i].green(),line3D[i].blue());
			if(entropy<threshold_entropy_mixed){line3D.removeAt(i);i--;}
		}
		
		//On ajoute les couleurs dans l'afficheur
		for(int i=0;i<sb_nbColorsAsked->value();i++)
		{
			if(i==0)
			{
				plotcolors->addColors(color2);
			}
			else if(i==(sb_nbColorsAsked->value()-1))
			{
				plotcolors->addColors(color1);
			}
			else
			{
				plotcolors->addColors(line3D[rand()%line3D.size()]);
			}
		}
		
	}
	else
	{
		le_question->setText(tr("Oups! I need a Fuzzy CLUT please..."));
	}
	parent->adjust();
}
void ModuleCLUTSurveyColorLimitsEntropy::s_clearResults()
{
	results.clear();
	te_results->clear();
	te_results->setText(results);
}

void ModuleCLUTSurveyColorLimitsEntropy::getBriSat(uchar r,uchar g,uchar b,uchar * L,uchar * S)
{
	uchar var_Min = std::min( std::min(r, g), b );    //Min. value of RGB
	uchar var_Max = std::max( std::max(r, g), b );    //Max. value of RGB
	uchar del_Max = var_Max - var_Min; 				//Delta RGB value

	*L = ( var_Max + var_Min ) / 2;

	if ( del_Max == 0 )                     //This is a gray, no chroma...
	{		                                //HSL results from 0 to 1
		*S = 0;
	}
	else                                    //Chromatic data...
	{
		if ( *L < 128 ) *S = (255 * del_Max) / ( var_Max + var_Min );
		else           *S = (255 * del_Max) / ( 512 - var_Max - var_Min );
	}
}
////////////////////////////////////////////////////////////////////////////////
void ModuleCLUTSurveyColorLimitsEntropy::s_showClassif(int display)
{
	plotcolors->displayClassif(display);
	plotcolors->update();
}
////////////////////////////////////////////////////////////////////////////////
void ModuleCLUTSurveyColorLimitsEntropy::bresenham_line_3D(QColor color1,QColor color2,QList<QColor> & colors)
{
	int x1=color1.red();
	int y1=color1.green();
	int z1=color1.blue(); 
	int x2=color2.red(); 
	int y2=color2.green(); 
	int z2=color2.blue();
	
    int i, dx, dy, dz, l, m, n, x_inc, y_inc, z_inc, err_1, err_2, dx2, dy2, dz2;
    int pixel[3];
	
    pixel[0] = x1;
    pixel[1] = y1;
    pixel[2] = z1;
    dx = x2 - x1;
    dy = y2 - y1;
    dz = z2 - z1;
    x_inc = (dx < 0) ? -1 : 1;
    l = abs(dx);
    y_inc = (dy < 0) ? -1 : 1;
    m = abs(dy);
    z_inc = (dz < 0) ? -1 : 1;
    n = abs(dz);
    dx2 = l << 1;
    dy2 = m << 1;
    dz2 = n << 1;

    if ((l >= m) && (l >= n)) {
        err_1 = dy2 - l;
        err_2 = dz2 - l;
        for (i = 0; i < l; i++) {
            colors+=QColor(pixel[0],pixel[1],pixel[2],CLUT(clut,pixel[0],pixel[1],pixel[2]));
            if (err_1 > 0) {
                pixel[1] += y_inc;
                err_1 -= dx2;
            }
            if (err_2 > 0) {
                pixel[2] += z_inc;
                err_2 -= dx2;
            }
            err_1 += dy2;
            err_2 += dz2;
            pixel[0] += x_inc;
        }
    } else if ((m >= l) && (m >= n)) {
        err_1 = dx2 - m;
        err_2 = dz2 - m;
        for (i = 0; i < m; i++) {
            colors+=QColor(pixel[0],pixel[1],pixel[2],CLUT(clut,pixel[0],pixel[1],pixel[2]));
            if (err_1 > 0) {
                pixel[0] += x_inc;
                err_1 -= dy2;
            }
            if (err_2 > 0) {
                pixel[2] += z_inc;
                err_2 -= dy2;
            }
            err_1 += dx2;
            err_2 += dz2;
            pixel[1] += y_inc;
        }
    } else {
        err_1 = dy2 - n;
        err_2 = dx2 - n;
        for (i = 0; i < n; i++) {
            colors+=QColor(pixel[0],pixel[1],pixel[2],CLUT(clut,pixel[0],pixel[1],pixel[2]));
            if (err_1 > 0) {
                pixel[1] += y_inc;
                err_1 -= dz2;
            }
            if (err_2 > 0) {
                pixel[0] += x_inc;
                err_2 -= dz2;
            }
            err_1 += dy2;
            err_2 += dx2;
            pixel[2] += z_inc;
        }
    }
    colors+=QColor(pixel[0],pixel[1],pixel[2],CLUT(clut,pixel[0],pixel[1],pixel[2]));
}
////////////////////////////////////////////////////////////////////////////////

Q_EXPORT_PLUGIN2(ModuleCLUTSurveyColorLimitsEntropy_plugin,ModuleCLUTSurveyColorLimitsEntropy);  